﻿// ***********************************************************************
// Assembly         : XQ.Extension
// Author           : xq-notebook
// Created          : 01-26-2019
//
// Last Modified By : xq-notebook
// Last Modified On : 01-26-2019
// ***********************************************************************
// <copyright file="DataTableExtensions.cs" company="xiqiang">
//     Copyright ©  2019
// </copyright>
// <summary></summary>
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;

namespace XQ.Extension
{
    /// <summary>
    /// Class DataTableExtend.
    /// </summary>
    public static class DataTableExtend
    {

        /// <summary>
        /// 获取第一行第一列
        /// </summary>
        /// <param name="this"></param>
        /// <returns></returns>
        public static object FirstRowCell(this DataTable @this)
        {
            if (@this == null) return null;
            if (@this.Rows.Count <= 0) return null;
            return @this.Rows[0][0];
        }


        /// <summary>
        /// 获取第一行第一列
        /// </summary>
        /// <param name="this"></param>
        /// <returns></returns>
        public static string FirstRowCellToStr(this DataTable @this)
        {
            return @this.FirstRowCell().ToStr();
        }

        /// <summary>
        /// 获取第一行第一列
        /// </summary>
        /// <param name="this"></param>
        /// <returns></returns>
        public static int FirstRowCellToInt(this DataTable @this)
        {
            return @this.FirstRowCell().ToStr().ToInt();
        }


        /// <summary>
        /// 是否有数据
        /// </summary>
        /// <param name="this">The this.</param>
        /// <returns><c>true</c> if the specified this has data; otherwise, <c>false</c>.</returns>
        public static bool HasData(this DataTable @this)
        {
            return @this.IsNullOrNoData() == false;
        }


        /// <summary>
        /// 是否包含列
        /// </summary>
        /// <param name="this">The dt.</param>
        /// <param name="colName">Name of the col.</param>
        /// <returns><c>true</c> if the specified col name has column; otherwise, <c>false</c>.</returns>
        public static bool HasColumn(this DataTable @this, string colName)
        {
            if (@this == null)
                return false;
            return @this.Columns.Contains(colName);
        }

        /// <summary>
        /// 返回
        /// </summary>
        /// <param name="this">The dt.</param>
        /// <returns>IList&lt;DataRow&gt;.</returns>
        public static IList<DataRow> RowList(this DataTable @this)
        {
            if (@this == null)
                return new List<DataRow>();
            return @this.Rows.OfType<DataRow>().ToList();
        }

        /// <summary>
        /// DataTable 是否为Null或者没有数据
        /// </summary>
        /// <param name="this">The tab.</param>
        /// <returns><c>true</c> if [is null or no data] [the specified tab]; otherwise, <c>false</c>.</returns>
        public static bool IsNullOrNoData(this DataTable @this)
        {
            if (@this == null)
                return true;
            if (@this.Rows.Count <= 0)
                return true;

            return false;
        }

        /// <summary>
        /// 合并表
        /// </summary>
        /// <param name="this">待合并的结果表</param>
        /// <param name="dt1">记录最多的表</param>
        /// <param name="dt2">待合并表1</param>
        /// <param name="dt3">待合并表2</param>
        /// <param name="fun1">表1和表2关联条件</param>
        /// <param name="fun2">The fun2.</param>
        /// <returns>DataTable.</returns>
        public static DataTable ManyTablToOne(this DataTable @this, DataTable dt1, DataTable dt2, DataTable dt3, Func<DataRow, DataRow, bool> fun1, Func<DataRow, DataRow, bool> fun2)
        {

            @this = BindTable(dt1, dt2);
            @this = BindTable(@this, dt3);
            var dt2ls = dt2.Rows.OfType<DataRow>().ToList();
            var dt3ls = dt3.Rows.OfType<DataRow>().ToList();

            for (int i = 0; i < dt1.Rows.Count; i++)
            {
                var row = @this.NewRow();
                var dtrow = dt1.Rows[i];
                row.ItemArray = dtrow.ItemArray;

                dt2ls.ForEach(o =>
                {
                    if (fun1(row, o))
                    {
                        RowDataToRowData(o, row);
                    }
                });

                dt3ls.ForEach(o =>
                {
                    if (fun2(row, o))
                    {
                        RowDataToRowData(o, row);
                    }
                });
                @this.Rows.Add(row);
            }

            return @this;
        }

        /// <summary>
        /// 字段组合
        /// </summary>
        /// <param name="this">The DT1.</param>
        /// <param name="dt2">The DT2.</param>
        /// <returns>DataTable.</returns>
        private static DataTable BindTable(DataTable @this, DataTable dt2)
        {
            var tab = @this.Clone();
            for (int i = 0; i < dt2.Columns.Count; i++)
            {
                var col = dt2.Columns[i].ColumnName;
                if (!tab.Columns.Contains(col))
                {
                    tab.Columns.Add(col);
                }
            }
            return tab;
        }

        /// <summary>
        /// 数据值拷贝
        /// </summary>
        /// <param name="fromRow">From row.</param>
        /// <param name="Torow">The torow.</param>
        private static void RowDataToRowData(DataRow fromRow, DataRow Torow)
        {
            var colsFrom = fromRow.Table.Columns;
            var colsTo = Torow.Table.Columns;
            colsFrom.OfType<DataColumn>().ToList().ForEach(o =>
            {
                var value = fromRow.CValueStr(o.ColumnName);
                if (XString.IsNullOrWhiteSpace(value))
                {
                    return;
                }
                if (!colsTo.Contains(o.ColumnName))
                {
                    return;
                }
                Torow[o.ColumnName] = fromRow[o.ColumnName];
            });
        }



        /// <summary>
        /// Enumerates to entities in this collection.
        /// </summary>
        /// <typeparam name="T">Generic type parameter.</typeparam>
        /// <param name="this">The @this to act on.</param>
        /// <returns>@this as an IEnumerable&lt;T&gt;</returns>
        public static IEnumerable<T> ToEntities<T>(this DataTable @this) where T : new()
        {
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);

            var list = new List<T>();

            foreach (DataRow dr in @this.Rows)
            {
                var entity = new T();

                foreach (PropertyInfo property in properties)
                {
                    try
                    {
                        if (@this.Columns.Contains(property.Name))
                        {
                            Type valueType = property.PropertyType;
                            var num = 0;
                            if (valueType == typeof(Int16) || valueType == typeof(Int32) || valueType == typeof(Int64))
                            {
                                var obj = dr[property.Name].To(valueType).ToStr();
                                if (int.TryParse(obj, out num))
                                {
                                    property.SetValue(entity, num, null);
                                }
                                else
                                {
                                    property.SetValue(entity, dr[property.Name].To(valueType), null);
                                }
                            }
                            else if (valueType == typeof(Int16?) || valueType == typeof(Int32?) || valueType == typeof(Int64?))
                            {
                                var obj = dr[property.Name].To(valueType);
                                if (obj == null)
                                {
                                    property.SetValue(entity, null, null);
                                }
                                else
                                {
                                    var val = obj.ToStr();
                                    if (int.TryParse(val, out num))
                                    {
                                        property.SetValue(entity, num, null);
                                    }
                                    else
                                    {
                                        property.SetValue(entity, dr[property.Name].To(valueType), null);
                                    }
                                }

                            }
                            else
                            {
                                property.SetValue(entity, dr[property.Name].To(valueType), null);
                            }

                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(property.Name + "转换失败：" + ex.Message);
                    }

                }

                foreach (FieldInfo field in fields)
                {
                    try
                    {
                        if (@this.Columns.Contains(field.Name))
                        {
                            Type valueType = field.FieldType;
                            var num = 0;
                            if (valueType == typeof(Int16) || valueType == typeof(Int32) || valueType == typeof(Int64))
                            {
                                var obj = dr[field.Name].To(valueType).ToStr();
                                if (int.TryParse(obj, out num))
                                {
                                    field.SetValue(entity, num);
                                }
                                else
                                {
                                    field.SetValue(entity, dr[field.Name].To(valueType));
                                }
                            }
                            else if (valueType == typeof(Int16?) || valueType == typeof(Int32?) || valueType == typeof(Int64?))
                            {
                                var obj = dr[field.Name].To(valueType);
                                if (obj == null)
                                {
                                    field.SetValue(entity, null);
                                }
                                else
                                {
                                    var val = obj.ToStr();
                                    if (int.TryParse(val, out num))
                                    {
                                        field.SetValue(entity, num);
                                    }
                                    else
                                    {
                                        field.SetValue(entity, dr[field.Name].To(valueType));
                                    }
                                }
                            }
                            else
                            {
                                field.SetValue(entity, dr[field.Name].To(valueType));
                            }

                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(field.Name + "转换失败：" + ex.Message);
                    }

                }

                list.Add(entity);
            }

            return list;
        }
    }
}
